package com.nidu.demo.oauth2.ability;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.cola.exception.BizException;
import com.nidu.demo.common.exception.ErrorCodeConstants;
import com.nidu.demo.oauth2.gateway.OAuth2AccessTokenGateway;
import com.nidu.demo.oauth2.gateway.OAuth2RefreshTokenGateway;
import com.nidu.demo.oauth2.model.OAuth2AccessToken;
import com.nidu.demo.oauth2.model.OAuth2RefreshToken;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Component
@RequiredArgsConstructor
public class OAuth2AccessTokenDomainService {

    private final OAuth2AccessTokenGateway gateway;

    private final OAuth2RefreshTokenGateway refreshTokenGateway;

    public OAuth2AccessToken validateAccessToken(String accessToken) {
        OAuth2AccessToken oAuth2AccessToken = gateway.getByAccessToken(accessToken);
        if (oAuth2AccessToken == null){
            throw new BizException(ErrorCodeConstants.TOKEN_INVALID.getCode(), ErrorCodeConstants.TOKEN_INVALID.getMessage());
        }
        oAuth2AccessToken.isExpired();
        return oAuth2AccessToken;
    }

    @Transactional
    public OAuth2AccessToken createToken(Long userId, Integer userType, Long clientId,
                                         List<String> scopes, Long accessTokenValiditySeconds,
                                         Long refreshTokenValiditySeconds) {
        // 保存刷新令牌
        OAuth2RefreshToken refreshToken = OAuth2RefreshToken.create(
                userId, userType, clientId, scopes,
                LocalDateTime.now().plusSeconds(refreshTokenValiditySeconds));
        refreshToken = refreshTokenGateway.create(refreshToken);

        // 保存访问令牌
        OAuth2AccessToken accessToken = OAuth2AccessToken.create(
                userId, userType, refreshToken.getRefreshToken(), clientId,
                scopes, LocalDateTime.now().plusSeconds(accessTokenValiditySeconds));
        return gateway.create(accessToken);
    }

    @Transactional
    public OAuth2AccessToken refreshAccessToken(Long userId, Integer userType,
                                                String refreshToken, Long clientId,
                                                List<String> scopes, Long validitySeconds) {
        List<OAuth2AccessToken> accessTokens = gateway.getByRefreshToken(refreshToken);
        if (CollUtil.isNotEmpty(accessTokens)) {
            Set<Long> ids = accessTokens.stream()
                    .filter(item -> item.getDeleted())
                    .map(OAuth2AccessToken::getId)
                    .collect(Collectors.toSet());
            gateway.deleteByIds(ids);
        }
        OAuth2AccessToken accessToken = OAuth2AccessToken.create(
                userId, userType, refreshToken, clientId, scopes,
                LocalDateTime.now().plusSeconds(validitySeconds));
        return gateway.create(accessToken);
    }

    @Transactional
    public void removeAccessToken(String accessToken) {
        OAuth2AccessToken token = gateway.getByAccessToken(accessToken);
        if (token == null) {
            throw new BizException(ErrorCodeConstants.TOKEN_INVALID.getCode(),
                    ErrorCodeConstants.TOKEN_INVALID.getMessage());
        }
        if (!token.getDeleted()) {
            gateway.deleteById(token.getId());
        }
        OAuth2RefreshToken refreshToken = refreshTokenGateway.getByRefreshToken(token.getRefreshToken());
        if (refreshToken == null) {
            throw new BizException(ErrorCodeConstants.REFRESH_TOKEN_INVALID.getCode(),
                    ErrorCodeConstants.REFRESH_TOKEN_INVALID.getMessage());
        }
        if (!token.getDeleted()) {
            refreshTokenGateway.deleteById(refreshToken.getId());
        }
    }

}
